package org.bud.excel.expot.ask;

import lombok.extern.java.Log;
import org.bud.excel.expot.bit.QueryArg;
import org.bud.excel.expot.bit.QueryPagerParam;
import org.bud.excel.expot.constant.Constant;
import org.bud.excel.expot.exception.ExcelException;
import org.bud.util.bit.Objects;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

@Log
public class ExportIterable<A,Z> implements Iterator<List<Z>> {

    private QueryArg<A,List<Z>> query;

    private String[] pager;

    private Long queryTotal;

    private Long pageSize;

    private Long pages;

    private Long retainPage;

    public ExportIterable<A,Z> query(QueryArg<A,List<Z>> query){
        this.query = query;
        check();
        init();
        return this;
    }

    @Override
    public boolean hasNext() {
        return retainPage > 0;
    }

    @Override
    public List<Z> next() {
        if(hasNext()){
            this.setPagers();
            retainPage--;
            List<Z> data = query.getData().apply(query.getQueryParam());
            return data;
        }
        throw new ExcelException("data all out");
    }

    /**
     * private function
     */
    private void check(){
        if(query.getPager() != null && query.getPager().length != 2){
            throw new ExcelException("check the query pager to match the [from],[pageSize]");
        }
        if(query.getQueryParam() == null){
            try{
                query.setQueryParam((A)new HashMap<>());
            } catch (Exception e){
                try{
                    query.setQueryParam((A)new QueryPagerParam());
                }catch (Exception e1){
                    throw new ExcelException("queryParam instanceof Map or QueryPagerParam",e);
                }

            }
        }
    }

    private void init() {
        pager = Optional.ofNullable(query.getPager())
                .orElseGet(() -> new String[]{Constant.FROM,Constant.PAGE_SIZE});
        queryTotal = Optional.ofNullable(query.getTotal())
                .map(o -> o.apply(query.getQueryParam()).longValue())
                .orElse(Constant.MAX_PER_PAGE_RECORD_NUM_NO_TOTAL);
        pageSize =  Optional.ofNullable(Optional.ofNullable(Constant.MAX_PER_PAGE_RECORD_NUM_NO_TOTAL)
                .filter(o -> query.getTotal() == null)
                .orElse(Constant.MAX_PER_PAGE_RECORD_NUM_HAS_TOTAL))
                .filter(o -> query.isAutoPage())
                .orElseGet(() -> Optional.ofNullable(query.getPerPage())
                        .orElseGet(() -> Optional.ofNullable(Objects.getValue(query.getQueryParam(),
                                pager[1])).map(o -> Long.getLong(o.toString()))
                                .orElseGet(() -> Optional.ofNullable(Constant.MAX_PER_PAGE_RECORD_NUM_NO_TOTAL)
                                        .filter(o -> query.getTotal() == null)
                                        .orElse(Constant.MAX_PER_PAGE_RECORD_NUM_HAS_TOTAL)))
                );
        pages = (queryTotal + pageSize - 1) / pageSize;

        retainPage = pages;
    }

    private void setPagers(){
        try{
            Objects.setValue(query.getQueryParam(),pager[0],(pages - retainPage) * pageSize);
            Objects.setValue(query.getQueryParam(),pager[1],pageSize);
        } catch (Exception e){
            log.warning(String.format("check if need set the %s,%s",pager[0],pager[1]));
        }

    }
}
